﻿using System;
using System.ComponentModel.DataAnnotations;
using System.Linq;
using System.Reflection;
using System.Web.UI;
using System.Web.UI.WebControls;

namespace FAfx.BootstrapWizard.Controls
{
    /// <summary>
    /// A validation control to use with DataAnnotations
    /// </summary>
    /// <see cref="http://www.ipreferjim.com/2010/05/system-componentmodel-dataannotations-for-asp-net-web-forms/"/>
    /// <seealso cref="http://djfr.wordpress.com/2013/04/23/validation-in-webforms-with-data-annotations/"/>
    [ToolboxData("<{0}:DataAnnotationValidator runat=\"server\" ControlToValidate=\"[Required]\" Display=\"Dynamic\" Text=\"*\" SourceTypeName=\"[FullyQualifiedTypeName]\" PropertyToValidate=\"[PropertyName]\" />")]
    public class DataAnnotationValidator : BaseValidator
    {
        /// <summary>
        /// THe Property that should be checked
        /// </summary>
        public string PropertyToValidate { get; set; }

        /// <summary>
        /// The object's type
        /// </summary>
        public string SourceTypeName { get; set; }

        protected override bool EvaluateIsValid()
        {
            Type source = GetValidatedType();
            PropertyInfo property = GetValidatedProperty(source);
            string value = GetControlValidationValue(ControlToValidate);

            foreach (ValidationAttribute va in property
                .GetCustomAttributes(typeof(ValidationAttribute), true)
                .OfType<ValidationAttribute>())
            {
                if (!va.IsValid(value))
                {
                    if (string.IsNullOrEmpty(ErrorMessage))
                    {
                        this.ErrorMessage = va.ErrorMessage;
                    }
                    //TODO: I think the va.ErrorMessage should be showing the DataAnnotation error message already. Odd.
                    if (string.IsNullOrEmpty(ErrorMessage))
                    {
                        // I am assuming that ALL DataAnnotation uses the displayName/propertyName 
                        // as the first parameter of its error
                        var displayAttribute=
                            property.GetCustomAttributes(typeof(DisplayAttribute))
                            .OfType<DisplayAttribute>()
                            .FirstOrDefault();

                        var displayName = (displayAttribute == null) ? property.Name : displayAttribute.Name; 

                        this.ErrorMessage = va.FormatErrorMessage(displayName);
                    }
                    return false;
                }
            }

            return true;
        }

        private Type GetValidatedType()
        {
            if (string.IsNullOrEmpty(SourceTypeName))
            {
                throw new InvalidOperationException("Null SourceTypeName can't be validated");
            }

            Type validatedType = Type.GetType(SourceTypeName);
            if (validatedType == null)
            {
                throw new InvalidOperationException(
                    string.Format("{0}:{1}", "Invalid SourceTypeName", SourceTypeName));
            }

            return validatedType;
        }

        private PropertyInfo GetValidatedProperty(Type source)
        {
            PropertyInfo property = source.GetProperty(PropertyToValidate,
              BindingFlags.Public | BindingFlags.Instance);

            if (property == null)
            {
                throw new InvalidOperationException(
                  string.Format("{0}:{1}", "Validated Property Does Not Exists", PropertyToValidate));
            }
            return property;
        }
    }


}